package labox.innovation.gameserver.model.actor.instance;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.concurrent.Future;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import labox.innovation.config.Config;
import labox.innovation.db.core.L2DatabaseFactory;
import labox.innovation.gameserver.ThreadPoolManager;
import labox.innovation.gameserver.ai.CtrlIntention;
import labox.innovation.gameserver.datatables.ItemTable;
import labox.innovation.gameserver.datatables.SkillTable;
import labox.innovation.gameserver.geodata.GeoData;
import labox.innovation.gameserver.handler.IItemHandler;
import labox.innovation.gameserver.handler.ItemHandler;
import labox.innovation.gameserver.idfactory.IdFactory;
import labox.innovation.gameserver.instancemanager.ItemsOnGroundManager;
import labox.innovation.gameserver.model.FuzeWorld;
import labox.innovation.gameserver.model.L2ItemInstance;
import labox.innovation.gameserver.model.L2Object;
import labox.innovation.gameserver.model.L2PetData;
import labox.innovation.gameserver.model.L2PetDataTable;
import labox.innovation.gameserver.model.L2Skill;
import labox.innovation.gameserver.model.Scene;
import labox.innovation.gameserver.model.actor.L2Character;
import labox.innovation.gameserver.model.actor.L2Summon;
import labox.innovation.gameserver.model.actor.stat.PetStat;
import labox.innovation.gameserver.model.itemcontainer.Inventory;
import labox.innovation.gameserver.model.itemcontainer.PcInventory;
import labox.innovation.gameserver.model.itemcontainer.PetInventory;
import labox.innovation.gameserver.network.SystemMessageId;
import labox.innovation.gameserver.network.serverpackets.ActionFailed;
import labox.innovation.gameserver.network.serverpackets.InventoryUpdate;
import labox.innovation.gameserver.network.serverpackets.ItemList;
//import labox.innovation.gameserver.network.serverpackets.PetInventoryUpdate;
//import labox.innovation.gameserver.network.serverpackets.PetItemList;
//import labox.innovation.gameserver.network.serverpackets.PetStatusShow;
import labox.innovation.gameserver.network.serverpackets.StatusUpdate;
import labox.innovation.gameserver.network.serverpackets.SystemMessage;
//import labox.innovation.gameserver.network.serverpackets.ValidateLocation;
import labox.innovation.gameserver.skills.Stats;
import labox.innovation.gameserver.taskmanager.DecayTaskManager;
import labox.innovation.gameserver.templates.chars.NpcTemplate;
import labox.innovation.gameserver.templates.item.L2EtcItemType;
import labox.innovation.gameserver.templates.item.L2Item;
import labox.innovation.gameserver.templates.item.L2Weapon;
import labox.innovation.gameserver.timer.Timer;
import labox.innovation.util.Rnd;

/**
 *
 * This class ...
 *
 * @version $Revision: 1.15.2.10.2.16 $ $Date: 2005/04/06 16:13:40 $
 */
public class L2PetInstance extends L2Summon {

	protected static final Logger _logPet = LoggerFactory.getLogger(L2PetInstance.class.getName());

	private int _curFed;

	private PetInventory _inventory;

	private final long _controlItemId;

	private boolean _respawned;

	private boolean _mountable;

	private Timer _feedTask;

	private int _weapon;

	private int _armor;

	private int _jewel;

	private L2PetData _data;

	/** The Experience before the last Death Penalty */
	private long _expBeforeDeath = 0;

	private int _curWeightPenalty = 0;

	private static final int PET_DECAY_DELAY = 86400000; // 24 hours

	private int _maxload;

	public final L2PetData getPetData() {
		if (_data == null)
			_data = L2PetDataTable.getInstance().getPetData(getTemplate()._id, getStat().getLevel());

		return _data;
	}

	public final void setPetData(L2PetData value) {
		_data = value;
	}

	/**
	 * Manage Feeding Task.<BR><BR>
	 *
	 * <B><U> Actions</U> :</B><BR>
	 * <li>Feed or kill the pet depending on hunger level</li>
	 * <li>If pet has food in inventory and feed level drops below 55% then consume food from inventory</li>
	 * <li>Send a broadcastStatusUpdate packet for this L2PetInstance</li><BR><BR>
	 *
	 */

	class FeedTask implements Runnable {
		public void run() {
			try {
				if (getOwner() == null || getOwner().getPet() == null || getOwner().getPet().getObjectId() != getObjectId()) {
					stopFeed();
					return;
				} else if (getCurrentFed() > getFeedConsume()) {
					// eat
					setCurrentFed(getCurrentFed() - getFeedConsume());
				} else {
					setCurrentFed(0);
				}

				int[] foodIds = L2PetDataTable.getFoodItemId(getTemplate()._id);
				if (foodIds[0] == 0)
					return;
				L2ItemInstance food = null;
				food = getInventory().getItemByItemId(foodIds[0]);

				// use better strider food if exists
				if (L2PetDataTable.isStrider(getNpcId())) {
					if (getInventory().getItemByItemId(foodIds[1]) != null)
						food = getInventory().getItemByItemId(foodIds[1]);
				}

				if (food != null && isHungry()) {
					IItemHandler handler = ItemHandler.getInstance().getItemHandler(food.getEtcItem());
					if (handler != null) {
						SystemMessage sm = new SystemMessage(SystemMessageId.PET_TOOK_S1_BECAUSE_HE_WAS_HUNGRY);
						sm.addItemName(food.getItemId());
						getOwner().sendPacket(sm);
						handler.useItem(L2PetInstance.this, food);
					}
				} else {
					if (getCurrentFed() == 0) {
						SystemMessage sm = new SystemMessage(SystemMessageId.YOUR_PET_IS_VERY_HUNGRY);
						getOwner().sendPacket(sm);
						if (Rnd.get(100) < 30) {
							stopFeed();
							sm = new SystemMessage(SystemMessageId.STARVING_GRUMPY_AND_FED_UP_YOUR_PET_HAS_LEFT);
							getOwner().sendPacket(sm);
							_log.info("Hungry pet deleted for player :" + getOwner().getName() + " Control Item Id :" + getControlItemId());
							deleteMe(getOwner());
						}
					} else if (getCurrentFed() < (0.11 * getPetData().getPetMaxFeed())) {
						SystemMessage sm = new SystemMessage(SystemMessageId.PET_CAN_RUN_AWAY_WHEN_HUNGER_BELOW_10_PERCENT);
						getOwner().sendPacket(sm);
						if (Rnd.get(100) < 3) {
							stopFeed();
							sm = new SystemMessage(SystemMessageId.STARVING_GRUMPY_AND_FED_UP_YOUR_PET_HAS_LEFT);
							getOwner().sendPacket(sm);
							_log.info("Hungry pet deleted for player :" + getOwner().getName() + " Control Item Id :" + getControlItemId());
							deleteMe(getOwner());
						}
					}
					broadcastStatusUpdate();
				}
			} catch (Exception e) {
				_logPet.error("Pet [ObjectId: " + getObjectId() + "] a feed task error has occurred", e);
			}
		}

		/**
		 * @return
		 */
		private int getFeedConsume() {
			// if pet is attacking
			if (isAttackingNow())
				return getPetData().getPetFeedBattle();
			else
				return getPetData().getPetFeedNormal();
		}
	}

	public synchronized static L2PetInstance spawnPet(NpcTemplate template, FuzePcInstance owner, L2ItemInstance control) {
		if (FuzeWorld.getInstance().getPet(owner.getObjectId()) != null)
			return null; // owner has a pet listed in world

		L2PetInstance pet = restore(control, template, owner);
		// add the pet instance to world
		if (pet != null) {
			pet.setTitle(owner.getName());
			FuzeWorld.getInstance().enterWorld(pet);
		}

		return pet;
	}

	public L2PetInstance(int objectId, NpcTemplate template, FuzePcInstance owner, L2ItemInstance control) {
		super(objectId, template, owner);

		_controlItemId = control.getObjectId();

		// Pet's initial level is supposed to be read from DB
		// Pets start at :
		// Wolf : Level 15
		// Hatcling : Level 35
		// Tested and confirmed on official servers
		// Sin-eaters are defaulted at the owner's level
		if (template._id == 12564)
			getStat().setLevel((byte) getOwner().getLevel());
		else
			getStat().setLevel(template.level);

		_inventory = new PetInventory(this);
//		_inventory.restore();

		int npcId = template._id;
		_mountable = L2PetDataTable.isMountable(npcId);
		_maxload = getPetData().getPetMaxLoad();
	}

	@Override
	public PetStat getStat() {
		return (PetStat) super.getStat();
	}

	@Override
	public void initCharStat() {
		setStat(new PetStat(this));
	}

	public boolean isRespawned() {
		return _respawned;
	}

	@Override
	public int getSummonType() {
		return 2;
	}

	@Override
	public void onAction(FuzePcInstance player) {
		// Aggression target lock effect
		if (player.isLockedTarget() && player.getLockedTarget() != this) {
			player.sendPacket(new SystemMessage(SystemMessageId.FAILED_CHANGE_TARGET));
			player.sendPacket(ActionFailed.STATIC_PACKET);
			return;
		}

		boolean isOwner = player.getObjectId() == getOwner().getObjectId();

//		if (isOwner && player != getOwner())
//			updateRefOwner(player);
//
//		if (this != player.getTarget()) {
//			if (_log.isDebugEnabled())
//				_log.debug("new target selected:{}", getObjectId());
//
//			// Set the target of the L2PcInstance player
//			player.setTarget(this);
//
//			// Send a Server->Client packet StatusUpdate of the L2PetInstance to the L2PcInstance to update its HP bar
//			StatusUpdate su = new StatusUpdate(getObjectId());
//			su.addAttribute(StatusUpdate.CUR_HP, (int) getCurrentHp());
//			su.addAttribute(StatusUpdate.MAX_HP, getMaxHp());
//			player.sendPacket(su);
//		}
//
//		// Check if the pet is attackable (without a forced attack) and isn't dead
//		if (isAutoAttackable(player) && !isOwner) {
//			if (GeoData.getInstance().canSeeTarget(player, this)) {
//				// Set the L2PcInstance Intention to AI_INTENTION_ATTACK
//				player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this);
//				player.onActionRequest();
//			}
//
//		} else if (!isInsideRadius(player, 150, false)) {
//			if (Config.GEODATA > 0) {
//				if (GeoData.getInstance().canSeeTarget(player, this)) {
//					player.getAI().setIntention(CtrlIntention.AI_INTENTION_INTERACT, this);
//					player.onActionRequest();
//				}
//			} else {
//				player.getAI().setIntention(CtrlIntention.AI_INTENTION_INTERACT, this);
//				player.onActionRequest();
//			}
//		} else {
//			if (isOwner)
//				player.sendPacket(new PetStatusShow(this));
//		}

		player.sendPacket(ActionFailed.STATIC_PACKET);
	}

	@Override
	public long getControlItemId() {
		return _controlItemId;
	}

	public L2ItemInstance getControlItem() {
		return getOwner().getInventory().getItemByObjectId(_controlItemId);
	}

	public int getCurrentFed() {
		return _curFed;
	}

	public void setCurrentFed(int num) {
		_curFed = num > getMaxFed() ? getMaxFed() : num;
	}

	/**
	 * Returns the pet's currently equipped weapon instance (if any).
	 */
	@Override
	public L2ItemInstance getActiveWeaponInstance() {
//		for (L2ItemInstance item : getInventory().getItems())
////			if (item.getLocation() == L2ItemInstance.ItemLocation.PET_EQUIP && item.getItem().getBodyPart() == L2Item.SLOT_R_HAND)
//				return item;

		return null;
	}

	/**
	 * Returns the pet's currently equipped weapon (if any).
	 */
	@Override
	public L2Weapon getActiveWeaponItem() {
		L2ItemInstance weapon = getActiveWeaponInstance();

		if (weapon == null)
			return null;

		return (L2Weapon) weapon.getItem();
	}

	@Override
	public L2ItemInstance getSecondaryWeaponInstance() {
		// temporary? unavailable
		return null;
	}

	@Override
	public L2Weapon getSecondaryWeaponItem() {
		// temporary? unavailable
		return null;
	}

	@Override
	public PetInventory getInventory() {
		return _inventory;
	}

	/**
	 * Destroys item from inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance.
	 * @param process : String Identifier of process triggering this action
	 * @param objectId : int Item Instance identifier of the item to be destroyed
	 * @param count : int Quantity of items to be destroyed
	 * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
	 * @param sendMessage : boolean Specifies whether to send message to Client about this action
	 * @return boolean informing if the action was successfull
	 */
//	@Override
	public boolean destroyItem(String process, int objectId, int count, L2Object reference, boolean sendMessage) {
		L2ItemInstance item = _inventory.destroyItem(process, objectId, count, getOwner(), reference);

		if (item == null) {
			if (sendMessage)
				getOwner().sendPacket(new SystemMessage(SystemMessageId.NOT_ENOUGH_ITEMS));

			return false;
		}

		// Send Pet inventory update packet
//		PetInventoryUpdate petIU = new PetInventoryUpdate();
//		petIU.addItem(item);
//		getOwner().sendPacket(petIU);

		if (sendMessage) {
			SystemMessage sm = new SystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
			sm.addItemName(item.getItemId());
			sm.addItemNumber(count);
			getOwner().sendPacket(sm);
		}
		return true;
	}

	/**
	 * Destroy item from inventory by using its <B>itemId</B> and send a Server->Client InventoryUpdate packet to the L2PcInstance.
	 * @param process : String Identifier of process triggering this action
	 * @param itemId : int Item identifier of the item to be destroyed
	 * @param count : int Quantity of items to be destroyed
	 * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
	 * @param sendMessage : boolean Specifies whether to send message to Client about this action
	 * @return boolean informing if the action was successfull
	 */
	@Override
	public boolean destroyItemByItemId(String process, int itemId, int count, L2Object reference, boolean sendMessage) {
		L2ItemInstance item = _inventory.destroyItemByItemId(process, itemId, count, getOwner(), reference);

		if (item == null) {
			if (sendMessage)
				getOwner().sendPacket(new SystemMessage(SystemMessageId.NOT_ENOUGH_ITEMS));
			return false;
		}

		// Send Pet inventory update packet
//		PetInventoryUpdate petIU = new PetInventoryUpdate();
//		petIU.addItem(item);
//		getOwner().sendPacket(petIU);

		if (sendMessage) {
			SystemMessage sm = new SystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
			sm.addItemName(itemId);
			sm.addItemNumber(count);
			getOwner().sendPacket(sm);
		}

		return true;
	}

	@Override
	protected void doPickupItem(L2Object object) {
		boolean follow = getFollowStatus();
		if (isDead())
			return;

		getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
//		StopMove sm = new StopMove(getObjectId(), getX(), getY());

//		if (Config.DEBUG)
//			_logPet.fine("Pet pickup pos: " + object.getX() + " " + object.getY());

//		broadcastPacket(sm);

//		if (!(object instanceof L2ItemInstance)) {
//			// dont try to pickup anything that is not an item :)
//			_logPet.warning("trying to pickup wrong target." + object);
//			getOwner().sendPacket(ActionFailed.STATIC_PACKET);
//			return;
//		}

		L2ItemInstance target = (L2ItemInstance) object;

		synchronized (target) {
			if (!target.isVisible()) {
				getOwner().sendPacket(ActionFailed.STATIC_PACKET);
				return;
			}
			if (!_inventory.validateCapacity(target)) {
				getOwner().sendPacket(new SystemMessage(SystemMessageId.YOUR_PET_CANNOT_CARRY_ANY_MORE_ITEMS));
				return;
			}
			if (!_inventory.validateWeight(target, target.getCount())) {
				getOwner().sendPacket(new SystemMessage(SystemMessageId.UNABLE_TO_PLACE_ITEM_YOUR_PET_IS_TOO_ENCUMBERED));
				return;
			}
			if (target.getOwnerId() != 0 && target.getOwnerId() != getOwner().getObjectId() && !getOwner().isInLooterParty(target.getOwnerId())) {
				getOwner().sendPacket(ActionFailed.STATIC_PACKET);

				if (target.getItemId() == 57) {
					SystemMessage smsg = new SystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1_ADENA);
					smsg.addItemNumber(target.getCount());
					getOwner().sendPacket(smsg);
				} else if (target.getCount() > 1) {
					SystemMessage smsg = new SystemMessage(SystemMessageId.FAILED_TO_PICKUP_S2_S1_S);
					smsg.addItemName(target.getItemId());
					smsg.addItemNumber(target.getCount());
					getOwner().sendPacket(smsg);
				} else {
					SystemMessage smsg = new SystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1);
					smsg.addItemName(target.getItemId());
					getOwner().sendPacket(smsg);
				}

				return;
			}
			if (target.getItemLootShedule() != null && (target.getOwnerId() == getOwner().getObjectId() || getOwner().isInLooterParty(target.getOwnerId())))
				target.resetOwnerTimer();

			target.pickupMe(this);

			if (Config.SAVE_DROPPED_ITEM) // item must be removed from ItemsOnGroundManager if is active
				ItemsOnGroundManager.getInstance().removeObject(target);
		}

		if (target.getItemId() == 57) {
			SystemMessage sm2 = new SystemMessage(SystemMessageId.PET_PICKED_S1_ADENA);
			sm2.addItemNumber(target.getCount());
			getOwner().sendPacket(sm2);
		} else if (target.getEnchantLevel() > 0) {
			SystemMessage sm2 = new SystemMessage(SystemMessageId.PET_PICKED_S1_S2);
			sm2.addNumber(target.getEnchantLevel());
			sm2.addString(target.getName());
			getOwner().sendPacket(sm2);
		} else if (target.getCount() > 1) {
			SystemMessage sm2 = new SystemMessage(SystemMessageId.PET_PICKED_S2_S1_S);
			sm2.addItemNumber(target.getCount());
			sm2.addString(target.getName());
			getOwner().sendPacket(sm2);
		} else {
			SystemMessage sm2 = new SystemMessage(SystemMessageId.PET_PICKED_S1);
			sm2.addString(target.getName());
			getOwner().sendPacket(sm2);
		}
		getInventory().addItem("Pickup", target, getOwner(), this);
		//FIXME Just send the updates if possible (old way wasn't working though)
//		PetItemList iu = new PetItemList(this);
//		getOwner().sendPacket(iu);

		getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);

		if (follow)
			followOwner();
	}

	@Override
	public void deleteMe(FuzePcInstance owner) {
		super.deleteMe(owner);
		destroyControlItem(owner, false); //this should also delete the pet from the db
	}

	@Override
	public boolean doDie(L2Character killer) {
		if (!super.doDie(killer))
			return false;
		stopFeed();
		getOwner().sendPacket(new SystemMessage(SystemMessageId.MAKE_SURE_YOU_RESSURECT_YOUR_PET_WITHIN_24_HOURS));
		onDecay();
		deathPenalty();
		return true;
	}

	@Override
	public void doRevive() {
		getOwner().removeReviving();

		super.doRevive();
		startFeed();
		getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE, null);
	}

	/**
	 * Transfers item to another inventory
	 * @param process : String Identifier of process triggering this action
	 * @param itemId : int Item Identifier of the item to be transfered
	 * @param count : int Quantity of items to be transfered
	 * @param actor : L2PcInstance Player requesting the item transfer
	 * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
	 * @return L2ItemInstance corresponding to the new item or the updated item in inventory
	 */
	public L2ItemInstance transferItem(String process, int objectId, long count, Inventory target, FuzePcInstance actor, L2Object reference) {
		L2ItemInstance oldItem = getInventory().getItemByObjectId(objectId);
//		L2ItemInstance newItem = getInventory().transferItem(process, objectId, count, target, actor, reference);
//
//		if (newItem == null)
//			return null;

		// Send inventory update packet
//		PetInventoryUpdate petIU = new PetInventoryUpdate();
//		if (oldItem.getCount() > 0 && oldItem != newItem)
//			petIU.addModifiedItem(oldItem);
//		else
//			petIU.addRemovedItem(oldItem);
//		getOwner().sendPacket(petIU);

		// Send target update packet
//		if (target instanceof PcInventory) {
//			FuzePcInstance targetPlayer = ((PcInventory) target).getOwner();
//			InventoryUpdate playerUI = new InventoryUpdate();
//			if (newItem.getCount() > count)
//				playerUI.addModifiedItem(newItem);
//			else
//				playerUI.addNewItem(newItem);
//			targetPlayer.sendPacket(playerUI);
//
//			// Update current load as well
//			StatusUpdate playerSU = new StatusUpdate(targetPlayer.getObjectId());
//			playerSU.addAttribute(StatusUpdate.CUR_LOAD, targetPlayer.getCurrentLoad());
//			targetPlayer.sendPacket(playerSU);
//		} else if (target instanceof PetInventory) {
//			petIU = new PetInventoryUpdate();
//			if (newItem.getCount() > count)
//				petIU.addRemovedItem(newItem);
//			else
//				petIU.addNewItem(newItem);
//			((PetInventory) target).getOwner().getOwner().sendPacket(petIU);
//		}
//		getInventory().refreshWeight();
		return null;
	}

	@Override
	public void giveAllToOwner() {
		try {
			Inventory petInventory = getInventory();
//			for (L2ItemInstance giveit : petInventory.getItems()) {
//				if (((giveit.getItem().getWeight() * giveit.getCount()) + getOwner().getInventory().getTotalWeight()) < getOwner().getMaxLoad()) {
//					// If the owner can carry it give it to them
//					giveItemToOwner(giveit);
//				} else {
//					// If they can't carry it, chuck it on the floor :)
//					dropItemHere(giveit);
//				}
//			}
		} catch (Exception e) {
			_logPet.error("Give all items error ", e);
		}
	}

	public void giveItemToOwner(L2ItemInstance item) {
		try {
			getInventory().transferItem("PetTransfer", item.getObjectId(), item.getCount(), getOwner().getInventory(), getOwner(), this);
//			PetInventoryUpdate petiu = new PetInventoryUpdate();
//			ItemList PlayerUI = new ItemList(getOwner(), false);
//			petiu.addRemovedItem(item);
//			getOwner().sendPacket(petiu);
//			getOwner().sendPacket(PlayerUI);
		} catch (Exception e) {
			_logPet.error("Error while giving item to owner: ", e);
		}
	}

	/**
	 * Remove the Pet from DB and its associated item from the player inventory
	 * @param owner The owner from whose invenory we should delete the item
	 */
	public void destroyControlItem(FuzePcInstance owner, boolean evolve) {
		// remove the pet instance from world
		FuzeWorld.getInstance().exitWorld(owner);

		// delete from inventory
		try {
			L2ItemInstance removedItem;
			if (evolve) {
				removedItem = owner.getInventory().destroyItem("Evolve", getControlItem(), 1, getOwner(), this);
			} else {
				removedItem = owner.getInventory().destroyItem("PetDestroy", getControlItem(), 1, getOwner(), this);
				owner.sendPacket(new SystemMessage(SystemMessageId.S1_DISAPPEARED).addItemName(removedItem));
			}

			InventoryUpdate iu = new InventoryUpdate();
			iu.addRemovedItem(removedItem);

			owner.sendPacket(iu);

//			StatusUpdate su = new StatusUpdate(owner.getObjectId());
//			su.addAttribute(StatusUpdate.CUR_LOAD, owner.getCurrentLoad());
//			owner.sendPacket(su);

			owner.broadcastUserInfo();

			FuzeWorld.getInstance().exitWorld(removedItem);
		} catch (Exception e) {
			_logPet.error("Error while destroying control item: ", e);
		}

		// pet control item no longer exists, delete the pet from the db
		Connection con = null;
		try {
			con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement statement = con.prepareStatement("DELETE FROM pets WHERE item_obj_id=?");
			statement.setLong(1, getControlItemId());
			statement.execute();
			statement.close();
		} catch (Exception e) {
			_logPet.error("Failed to delete Pet [ObjectId: " + getObjectId() + "]", e);
		} finally {
			try {
				con.close();
			} catch (Exception e) {
			}
		}
	}

	public void dropAllItems() {
		try {
			for (L2ItemInstance item : getInventory().getItems()) {
				dropItemHere(item);
			}
		} catch (Exception e) {
			_logPet.error("Pet Drop Error: " + e);
		}
	}

	public void dropItemHere(L2ItemInstance dropit) {
		dropit = getInventory().dropItem("Drop", dropit.getObjectId(), dropit.getCount(), getOwner(), this);

		if (dropit != null) {
			_logPet.info("Item id to drop: " + dropit.getItemId() + " amount: " + dropit.getCount());
			dropit.dropMe(this, getX(), getY());
		}
	}

	/** @return Returns the mount able. */
	@Override
	public boolean isMountable() {
		return _mountable;
	}

	private static L2PetInstance restore(L2ItemInstance control, NpcTemplate template, FuzePcInstance owner) {
		Connection con = null;
		try {
//			L2PetInstance pet;
//			if (template.type.compareToIgnoreCase("L2BabyPet") == 0)
//				pet = new L2BabyPetInstance(IdFactory.getInstance().getNextId(IdFactory.ID_SUMMON), template, owner, control);
//			else
//				pet = new L2PetInstance(IdFactory.getInstance().getNextId(IdFactory.ID_SUMMON), template, owner, control);
//
//			con = L2DatabaseFactory.getInstance().getConnection();
//			PreparedStatement statement = con.prepareStatement("SELECT item_obj_id, name, level, curHp, curMp, exp, sp, fed, weapon, armor, jewel FROM pets WHERE item_obj_id=?");
//			statement.setInt(1, control.getObjectId());
//			ResultSet rset = statement.executeQuery();
//			if (!rset.next()) {
//				rset.close();
//				statement.close();
//				return pet;
//			}
//
//			pet._respawned = true;
//			pet.setName(rset.getString("name"));
//
//			pet.getStat().setLevel(rset.getByte("level"));
//			pet.getStat().setExp(rset.getLong("exp"));
//			pet.getStat().setSp(rset.getInt("sp"));
//
//			pet.getStatus().setCurrentHp(rset.getDouble("curHp"));
//			pet.getStatus().setCurrentMp(rset.getDouble("curMp"));
//			pet.getStatus().setCurrentCp(pet.getMaxCp());
//			pet.setWeapon(rset.getInt("weapon"));
//			pet.setArmor(rset.getInt("armor"));
//			pet.setJewel(rset.getInt("jewel"));
//			if (rset.getDouble("curHp") < 0.5) {
//				pet.setIsDead(true);
//				pet.stopHpMpRegeneration();
//			}
//
//			pet.setCurrentFed(rset.getInt("fed"));
//
//			rset.close();
//			statement.close();
//			return pet;
		} catch (Exception e) {
			_logPet.error("could not restore pet data: ", e);
			return null;
		} finally {
			try {
				con.close();
			} catch (Exception e) {
			}
		}
		return null;
	}

	@Override
	public void store() {
		if (getControlItemId() == 0) {
			// this is a summon, not a pet, don't store anything
			return;
		}

		String req;
		if (!isRespawned())
			req = "INSERT INTO pets (name,level,curHp,curMp,exp,sp,fed,weapon,armor,jewel,item_obj_id) " + "VALUES (?,?,?,?,?,?,?,?,?,?,?)";
		else
			req = "UPDATE pets SET name=?,level=?,curHp=?,curMp=?,exp=?,sp=?,fed=?,weapon=?,armor=?,jewel=? " + "WHERE item_obj_id = ?";
		Connection con = null;
		try {
			con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement statement = con.prepareStatement(req);
			statement.setString(1, getName());
			statement.setInt(2, getStat().getLevel());
			statement.setDouble(3, getStatus().getCurrentHp());
			statement.setDouble(4, getStatus().getCurrentMp());
			statement.setLong(5, getStat().getExp());
//			statement.setInt(6, getStat().getSp());
			statement.setInt(7, getCurrentFed());
			statement.setInt(8, getWeapon());
			statement.setInt(9, getArmor());
			statement.setInt(10, getJewel());
//			statement.setInt(11, getControlItemId());
			statement.executeUpdate();
			statement.close();
			_respawned = true;
		} catch (Exception e) {
			_logPet.error("Failed to store Pet [ObjectId: " + getObjectId() + "] data", e);
		} finally {
			try {
				con.close();
			} catch (Exception e) {
			}
		}

		L2ItemInstance itemInst = getControlItem();
		if (itemInst != null && itemInst.getEnchantLevel() != getStat().getLevel()) {
			itemInst.setEnchantLevel(getStat().getLevel());
//			itemInst.updateDatabase();
		}
	}

	public synchronized void stopFeed() {
		if (_feedTask != null) {
			_feedTask.cancel();
			_feedTask = null;
			if (_logPet.isDebugEnabled())
				_logPet.debug("Pet [#" + getObjectId() + "] feed task stop");
		}
	}

	public synchronized void startFeed() {
		// stop feeding task if its active
		stopFeed();
		if (!isDead() && getOwner().getPet() == this) {
			_feedTask = new Timer(100000, true, new FeedTask());
		}
	}

	@Override
	public synchronized void unSummon(FuzePcInstance owner) {
		stopFeed();
//		stopHpMpRegeneration();
		super.unSummon(owner);

		if (!isDead())
			FuzeWorld.getInstance().exitWorld(owner);
	}

	/**
	 * Restore the specified % of experience this L2PetInstance has lost.<BR><BR>
	 */
	public void restoreExp(double restorePercent) {
		if (_expBeforeDeath > 0) {
			// Restore the specified % of lost experience.
//			getStat().addExp(Math.round((_expBeforeDeath - getStat().getExp()) * restorePercent / 100));
			_expBeforeDeath = 0;
		}
	}

	private void deathPenalty() {
		// TODO Need Correct Penalty

		int lvl = getStat().getLevel();
		double percentLost = -0.07 * lvl + 6.5;

		// Calculate the Experience loss
		long lostExp = Math.round((getStat().getExpForLevel(lvl + 1) - getStat().getExpForLevel(lvl)) * percentLost / 100);

		// Get the Experience before applying penalty
		_expBeforeDeath = getStat().getExp();

		// Set the new Experience value of the L2PetInstance
		getStat().addExp((int) -lostExp);
	}

	@Override
	public void addExp(int addToExp) {
		if (getNpcId() == 12564) //SinEater
			getStat().addExp(Math.round(addToExp * Config.SINEATER_XP_RATE));
		else
			getStat().addExp(Math.round(addToExp * Config.PET_XP_RATE));
	}

	@Override
	public final int getLevel() {
		return getStat().getLevel();
	}

	public int getMaxFed() {
		return getStat().getMaxFeed();
	}

	@Override
	public int getAccuracy() {
		return getStat().getAccuracy();
	}

//	@Override
	public int getCriticalHit(L2Character target, L2Skill skill) {
//		return getStat().getCriticalHit(target, skill);
		return -1;
	}

//	@Override
	public int getEvasionRate(L2Character target) {
//		return getStat().getEvasionRate(target);
		return -1;
	}

	@Override
	public int getRunSpeed() {
		return getStat().getRunSpeed();
	}

	@Override
	public int getPAtkSpd() {
		return getStat().getPAtkSpd();
	}

//	@Override
	public int getMAtkSpd() {
//		return getStat().getMAtkSpd();
		return -1;
	}

//	@Override
	public int getMAtk(L2Character target, L2Skill skill) {
//		return getStat().getMAtk(target, skill);
		return -1;
	}

//	@Override
	public int getMDef(L2Character target, L2Skill skill) {
//		return getStat().getMDef(target, skill);
		return -1;
	}

//	@Override
	public int getPAtk(L2Character target) {
//		return getStat().getPAtk(target);
		return -1;
	}

//	@Override
	public int getPDef(L2Character target) {
//		return getStat().getPDef(target);
		return -1;
	}

	@Override
	public final int getSkillLevel(int skillId) {
		if (getKnownSkill(skillId) == null)
			return -1;

		final int lvl = getLevel();
		return lvl > 70 ? 7 + (lvl - 70) / 5 : lvl / 10;
	}

	public void updateRefOwner(FuzePcInstance owner) {
//		int oldOwnerId = getOwner().getObjectId();
//
//		setOwner(owner);
//		FuzeWorld.getInstance().removePet(oldOwnerId);
//		FuzeWorld.getInstance().addPet(oldOwnerId, this);
	}

	public int getCurrentLoad() {
//		return _inventory.getTotalWeight();
		return -1;
	}

	public final void setMaxLoad(int maxLoad) {
		_maxload = maxLoad;
	}

	@Override
	public final int getMaxLoad() {
		return _maxload;
	}

	public int getInventoryLimit() {
		return Config.INVENTORY_MAXIMUM_PET;
	}

	@Override
	public final boolean isHungry() {
		return getCurrentFed() < (0.55 * getPetData().getPetMaxFeed());
	}

	public final void setWeapon(int id) {
		_weapon = id;
	}

	public final void setArmor(int id) {
		_armor = id;
	}

	public final void setJewel(int id) {
		_jewel = id;
	}

	public final int getWeapon() {
		return _weapon;
	}

	public final int getArmor() {
		return _armor;
	}

	public final int getJewel() {
		return _jewel;
	}

	@Override
	public int getPetSpeed() {
		return getPetData().getPetSpeed();
	}
}
